#include "rest_server.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <exception>
#include <httplib.h>
#include <json/json.h>

using namespace TakeAwayPlatform;

RestServer::RestServer(DatabaseHandler& dbHandler, DishModule& dishModule, OrderModule& orderModule, CartModule& cartModule)
    : dbHandler(dbHandler), dishModule(dishModule), orderModule(orderModule), cartModule(cartModule), threadPool(std::thread::hardware_concurrency()) 
{
    std::cout << "RestServer starting." << std::endl;
    std::cout.flush();
}

RestServer::~RestServer() 
{
    stop();
}

void RestServer::start(int port) 
{
    if (isRunning) {
        std::cerr << "Server is already running." << std::endl;
        std::cout.flush();
        return;
    }
    
    isRunning = true;
    stopRequested = false;
    
    serverThread = std::thread([this, port] {
        this->run_server(port);
    });
    
    std::cout << "Server starting on port " << port << "..." << std::endl;
    std::cout.flush();
}

void RestServer::run_server(int port) 
{
    try 
    {
        setup_routes();
        
        std::cout << "HTTP server listening on port " << port << std::endl;
        std::cout.flush();

        if (!server.listen("0.0.0.0", port)) {
            std::cerr << "Failed to start server on port " << port << std::endl;
        }
        
        std::cout << "HTTP server exited listen loop." << std::endl;
    } 
    catch (const std::exception& e) 
    {
        std::cerr << "Server error in worker thread: " << e.what() << std::endl;
        std::cout.flush();
    }
    
    isRunning = false;
    
    stopCv.notify_one();
    std::cout << "Server worker thread exiting." << std::endl;
    std::cout.flush();
}

void RestServer::stop() 
{
    if (!isRunning) {
        std::cout << "Server already stopped." << std::endl;
        std::cout.flush();
        return;
    }
    
    std::cout << "Requesting server stop..." << std::endl;
    std::cout.flush();
    stopRequested = true;
    
    server.stop();
    
    std::unique_lock<std::mutex> lock(stopMtx);
    if (stopCv.wait_for(lock, std::chrono::seconds(5), [this] {
        return !isRunning;
    }))
    {
        if (serverThread.joinable()) 
        {
            serverThread.join();
        }

        std::cout << "Server stopped successfully." << std::endl;
        std::cout.flush();
    } 
    else 
    {
        std::cerr << "Warning: Server did not stop within timeout." << std::endl;
        std::cout.flush();

        if (serverThread.joinable()) 
        {
            serverThread.detach();
        }
    }
}

bool RestServer::is_running() const 
{ 
    return isRunning;
}

void RestServer::setup_routes() 
{
    server.Get("/", [](const httplib::Request&, httplib::Response& res) {
        res.set_content("TakeAwayPlatform is running!", "text/plain");
    });
    
    server.Get("/health", [this](const httplib::Request&, httplib::Response& res) {
        if (this->is_running() && !this->stopRequested) {
            res.set_content("OK", "text/plain");
        } else {
            res.set_content("SHUTTING_DOWN", "text/plain");
            res.status = 503; // Service Unavailable
        }
    });

    server.Get("/dishes", [this](const httplib::Request&, httplib::Response& res) 
    {
        Json::Value result = dishModule.getDishesByMerchant(1);
        res.set_content(result.toStyledString(), "application/json");
    });

    server.Post("/order", [this](const httplib::Request& req, httplib::Response& res) 
    {
        Json::Value order = parse_json(req.body);
        int user_id = order["user_id"].asInt();
        int merchant_id = order["merchant_id"].asInt();
        int address_id = order["address_id"].asInt();
        double total_amount = order["total_amount"].asDouble();
        std::vector<std::pair<int, int>> cart_items;

        for (const auto& item : order["cart_items"]) {
            cart_items.push_back({item["dish_id"].asInt(), item["quantity"].asInt()});
        }

        if (orderModule.createOrder(user_id, merchant_id, address_id, cart_items, total_amount)) {
            res.set_content("{\"status\":\"created\"}", "application/json");
        } else {
            res.status = 500;
            res.set_content("{\"status\":\"error\"}", "application/json");
        }
    });

    server.Get("/cart", [this](const httplib::Request& req, httplib::Response& res) 
    {
        int user_id = std::stoi(req.get_param_value("user_id"));
        std::vector<std::pair<int, int>> cart_items;
        cartModule.getCartItems(user_id, cart_items);

        Json::Value result;
        for (const auto& item : cart_items) {
            Json::Value cart_item;
            cart_item["dish_id"] = item.first;
            cart_item["quantity"] = item.second;
            result.append(cart_item);
        }

        res.set_content(result.toStyledString(), "application/json");
    });

    server.Post("/cart/add", [this](const httplib::Request& req, httplib::Response& res) 
    {
        Json::Value data = parse_json(req.body);
        int user_id = data["user_id"].asInt();
        int dish_id = data["dish_id"].asInt();
        int quantity = data["quantity"].asInt();

        if (cartModule.addItemToCart(user_id, dish_id, quantity)) {
            res.set_content("{\"status\":\"added\"}", "application/json");
        } else {
            res.status = 500;
            res.set_content("{\"status\":\"error\"}", "application/json");
        }
    });

    server.Post("/cart/remove", [this](const httplib::Request& req, httplib::Response& res) 
    {
        Json::Value data = parse_json(req.body);
        int user_id = data["user_id"].asInt();
        int dish_id = data["dish_id"].asInt();

        if (cartModule.removeItemFromCart(user_id, dish_id)) {
            res.set_content("{\"status\":\"removed\"}", "application/json");
        } else {
            res.status = 500;
            res.set_content("{\"status\":\"error\"}", "application/json");
        }
    });
}

Json::Value RestServer::parse_json(const std::string& jsonStr) 
{
    Json::Value root;
    Json::CharReaderBuilder builder;
    std::string errors;
    std::istringstream json_stream(jsonStr);
    
    if (!Json::parseFromStream(builder, json_stream, &root, &errors)) 
    {
        throw std::runtime_error("JSON parse error: " + errors);
    }

    return root;
}